<?php

class Common_DataTable extends Common_DataObject {

    protected $arr_headers = array();
    protected $arr_footers = array();
    protected $arr_groups = array();

    protected $obj_view = null;
    protected $obj_model = null;
    protected $arr_summaries = array();

    public function __construct() {
        parent::__construct();
        $this->obj_view = new Common_DataTable_View_Html(); 
    }
    
    public function bindData($mix_data, $arr_options = array()) {

        $obj_model_factory = $this->getModelFactory();
        
        $enm_strategy = $obj_model_factory->detectStrategy($mix_data);
        $obj_model = $obj_model_factory->create($enm_strategy, $mix_data, $arr_options);
        
        if(!is_null($obj_model)) {
            
            $this->setModel($obj_model);
            
            // auto-detect headers
            if(
                !$this->hasHeaders() &&
                ($obj_model instanceof Common_DataTable_Model_SelfDefining)
            ) {
                $arr_headers = $obj_model->defineHeaders();
                $this->setHeaders($arr_headers);
            }
            
        }

    }
    
    protected function getModelFactory() {
        return new Common_DataTable_Model_Factory();
    }
    
    protected function setModel($obj_model) {
        $this->obj_model = $obj_model;
    }
    
    protected function getModel() {
        return $this->obj_model;
    }
    
    public function toArray() {
        return $this->obj_model->toArray();
    }
    
    public function getView() {
        return $this->obj_view;
    }
    
    public function switchView($mix_strategy) {
        if(is_object($mix_strategy) && ($mix_strategy instanceof Common_DataTable_View)) {
            $this->obj_view = $mix_strategy;
        }
    }
    
    public function getHeaders() {
        return $this->arr_headers;
    }

    public function setHeaders($arr_headers) {
        if(is_array($arr_headers)) {
            $this->arr_headers = $arr_headers;
        }
    }

    public function hasHeaders() {
        return !empty($this->arr_headers);
    }
    
    public function setFooters($arr_footers) {
        if(is_array($arr_footers)) {
            foreach($arr_footers as $arr) {
                $this->addFooter($arr);
            }
        }
    }
    
    public function addFooter($arr_footer) {
        $obj_summary = new Common_DataTable_Summary_Custom($arr_footer);
        $this->addDataSummary($obj_summary);
    }

    public function getColumns() {
        
        $arr_columns = array();
        
        $arr_headers = $this->getHeaders();
        if(!empty($arr_headers)) {
            
            $arr_columns = end($arr_headers);
            if(!is_array($arr_columns)) {
                $arr_columns = array();
            }
            
        }
        
        return $arr_columns;
    }

    public function getColumnIndexForKey($str_column_key) {

        $int_index = null;

        $arr_columns = $this->getColumns();
        $arr_column_keys = array_keys($arr_columns);

        $i = 0;
        foreach($arr_column_keys as $str_key) {
            if($str_key == $str_column_key) {
                $int_index = $i;
                break;
            }
            $i++;
        }

        return $int_index;
    }

    public function getColumn($columnKey) {

        $column = null;

        $arr_columns = $this->getColumns();

        foreach($arr_columns as $str_key => $columnMeta) {
            if($str_key == $columnKey) {
                $column = $columnMeta;
                break;
            }
        }

        return $column;
    }
    
    // todo: exception if model can't handle sorting
    public function sortDataBy($mix_sort_meta) {
        
        $arr_sort_meta = Utility_SortFunctions::standardise_sort_meta($mix_sort_meta);
 
        $arr_data = $this->getModel()->sortDataBy($arr_sort_meta);
        
        $this->setProperty('sorting', $arr_sort_meta);
        
    }
    
    public function hasData() {
        return $this->getModel()->hasData();
    }
    
    public function isDataSorted() {
        return ($this->getProperty('sorting', false) !== false);
    }
    
    // todo: exception if model can't handle grouping
    public function groupDataBy($mix_grouping, $arr_group_data = null) {
        
        $this->getModel()->groupDataBy($mix_grouping);
        
        if(!empty($arr_group_data)) {
            $this->setGroupData($arr_group_data);
        }
        
        $this->setProperty('grouping', $mix_grouping);
        
    }
    
    public function isDataGrouped() {
        return ($this->getProperty('grouping', false) !== false);
    }
    
    public function getDataGroupingDefinition() {
        return $this->getProperty('grouping', null);
    }
    
    public function setGroupData($arr_group_data) {
        if(is_array($arr_group_data)) {
            foreach($arr_group_data as $str_group_key => $arr_group) {
                $this->addGroupData($str_group_key, $arr_group);
            }
        }
    }
    
    public function addGroupData($str_group_key, $arr_group_data) {
        if(is_array($arr_group_data)) {
            $obj_summary = new Common_DataTable_Summary_Custom($arr_group_data, $str_group_key);
            $this->addDataSummary($obj_summary);
        }
    }
    
    public function addDataSummary(Common_DataTable_Summary $obj_summary) {
        $this->getModel()->addDataSummary($obj_summary);
    }

    public function render($arr_options = array()) {
     
        $str_output = '';
        if($this->hasData()) {

            $int_offset = isset($arr_options['offset'])
                ? $arr_options['offset']
                : null;
                
            $int_size = isset($arr_options['size']) 
                ? $arr_options['size']
                : null;
            
            if(is_integer($int_size) && is_null($int_offset)) {
                $int_offset = 0;
            }
            
            $arr_headers = $this->getHeaders();
            
            $arr_data = $this->getModel()->getDataModel($arr_headers, $int_size, $int_offset);
            
            $arr_footers = isset($arr_data['footers']) ? $arr_data['footers'] : array();
            $arr_properties = $this->getProperties();
  
            $arr_data = array_merge(array(
                'offset' => $int_offset,
                'size' => $int_size
            ), $arr_data);
            
            $str_output = $this->obj_view->publish($arr_data, $arr_headers, $arr_footers, $arr_properties, $arr_options);
        }
        
        return $str_output;
    }
    
    static public function remove_headers($arr_headers, $mix_column_keys, $arr_options = array()) {

        $arr_columns_to_remove = (array) $mix_column_keys;

        if(isset($arr_options['include_headers'])) {
            $arr_headers_to_include = (array) $arr_options['include_headers'];

            foreach($arr_columns_to_remove as $str_index => $str_grouping_key) {
                if(in_array($str_grouping_key, $arr_headers_to_include)) {
                    unset($arr_columns_to_remove[$str_index]);
                }
            }
        }


        $arr_columns = array_pop($arr_headers);

        //--------------------------------------------------------------------------
        // shift grouped headers according to the grouping-column

        // determine the grouping-column index
        $arr_indexes_to_remove = array();
        $i = 0;
        foreach ($arr_columns as $str_column_key => $arr_column) {

            foreach($arr_columns_to_remove as $mix_column_reference => $str_column_to_remove) {
                if ($str_column_key == $str_column_to_remove) {
                    $arr_indexes_to_remove[$i] = $i;
                }
                if(!is_integer($mix_column_reference) && ($str_column_key == $mix_column_reference)) {
                    $arr_indexes_to_remove[$i] = $i;
                }
            }

            $i++;
        }

        // modify all header rows to delete the corresponding header, or to decrement
        // the child counter
        foreach ($arr_headers as $i => $arr_header_row) {
            
            $int_header_index = 0;
            foreach ($arr_header_row as $str_column_key => $arr_header) {

                $int_children = isset($arr_header['children']) ? (int) $arr_header['children'] : 1;

                // does the header cover the column range in question?
                foreach($arr_indexes_to_remove as $int_column_index) {
                    
                    if (
                        ($int_header_index <= $int_column_index) &&
                        ($int_column_index <= ($int_header_index + $int_children -1))
                    ) {
                        
                        // case: this header covers more than 1 child, so decrement this counter
                        if(isset($arr_headers[$i][$str_column_key]['children']) && ($arr_headers[$i][$str_column_key]['children'] > 1)) {
                            $arr_headers[$i][$str_column_key]['children'] = $arr_header['children'] - 1;
                        }

                        // case: this header has only 1 child, so just delete it
                        else {
                            unset($arr_headers[$i][$str_column_key]);
                        }
                    }
                }

                $int_header_index += $int_children;
            }
        }


        foreach($arr_columns_to_remove as $mix_column_reference => $str_column_to_remove) {
            unset($arr_columns[$str_column_to_remove]);
            if(!is_integer($mix_column_reference)) {
                unset($arr_columns[$mix_column_reference]);
            }
        }

        array_push($arr_headers, $arr_columns);

        return $arr_headers;

    }
    
    /*
    public function store($str_id = null) {
    
        if(empty($str_id) || !is_string($str_id)) {
            $str_id = md5(serialize($this));
        }
        
        $obj_cache = Common_Cache_Factory::create(Common_Cache_Factory::STRATEGY_FILE);
        
        $str_cache_key = 'MTL_DATATABLE_' . $str_id;
        
        $obj_cache->save($str_cache_key, $this);
        
        return $str_id;
    } 
    
    static public function retrieve($str_id) {
    
        $obj_cache = Common_Cache_Factory::create(Common_Cache_Factory::STRATEGY_FILE);
        
        $str_cache_key = 'MTL_DATATABLE_' . $str_id;
        
        $mix = null;
        if($obj_cache->test($str_cache_key)) {
            $mix = $obj_cache->load($str_cache_key);
        }
        
        return $mix;
    }*/

}
